FluentCompositeKeyMappingConfigurationSupport.java
package org.codefilarete.stalactite.engine.configurer;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import org.codefilarete.reflection.AccessorByMethodReference;
import org.codefilarete.reflection.AccessorChain;
import org.codefilarete.reflection.AccessorDefinition;
import org.codefilarete.reflection.Accessors;
import org.codefilarete.reflection.MethodReferenceCapturer;
import org.codefilarete.reflection.MethodReferenceDispatcher;
import org.codefilarete.reflection.MutatorByMethodReference;
import org.codefilarete.reflection.ReadWriteAccessPoint;
import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
import org.codefilarete.reflection.SerializableAccessor;
import org.codefilarete.reflection.SerializableMutator;
import org.codefilarete.reflection.SerializablePropertyAccessor;
import org.codefilarete.reflection.SerializablePropertyMutator;
import org.codefilarete.reflection.ValueAccessPoint;
import org.codefilarete.reflection.ValueAccessPointMap;
import org.codefilarete.reflection.ValueAccessPointSet;
import org.codefilarete.stalactite.dsl.embeddable.ImportedEmbedOptions;
import org.codefilarete.stalactite.dsl.key.CompositeKeyMappingConfiguration;
import org.codefilarete.stalactite.dsl.key.CompositeKeyMappingConfigurationProvider;
import org.codefilarete.stalactite.dsl.key.CompositeKeyPropertyOptions;
import org.codefilarete.stalactite.dsl.key.FluentCompositeKeyMappingBuilder;
import org.codefilarete.stalactite.dsl.key.FluentCompositeKeyMappingConfiguration;
import org.codefilarete.stalactite.dsl.naming.ColumnNamingStrategy;
import org.codefilarete.stalactite.sql.ddl.Size;
import org.codefilarete.stalactite.sql.ddl.structure.Column;
import org.codefilarete.stalactite.sql.statement.binder.ParameterBinder;
import org.codefilarete.stalactite.sql.statement.binder.ParameterBinderRegistry.EnumBindType;
import org.codefilarete.tool.Reflections;
import org.codefilarete.tool.function.SerializableThrowingFunction;
/**
* @author Guillaume Mary
*/
public class FluentCompositeKeyMappingConfigurationSupport<C> implements FluentCompositeKeyMappingBuilder<C>, LambdaMethodUnsheller,
CompositeKeyMappingConfiguration<C> {
private CompositeKeyMappingConfigurationProvider<? super C> superMappingBuilder;
/** Owning class of mapped properties */
private final Class<C> classToPersist;
@Nullable
private ColumnNamingStrategy columnNamingStrategy;
/** Mapping definitions */
protected final List<CompositeKeyLinkage<C, ?>> mapping = new ArrayList<>();
/** Collection of embedded elements, even inner ones to help final build process */
private final Collection<Inset<C, ?>> insets = new ArrayList<>();
/** Last embedded element, introduced to help inner embedding registration (kind of algorithm help). Has no purpose in whole mapping configuration. */
private Inset<C, ?> currentInset;
/** Helper to unshell method references */
private final MethodReferenceCapturer methodSpy;
/**
* Creates a builder to map the given class for persistence
*
* @param classToPersist the class to create a mapping for
*/
public FluentCompositeKeyMappingConfigurationSupport(Class<C> classToPersist) {
this.classToPersist = classToPersist;
// Helper to capture Method behind method reference
this.methodSpy = new MethodReferenceCapturer();
}
@Override
public Class<C> getBeanType() {
return classToPersist;
}
@Override
public <O> Collection<Inset<C, O>> getInsets() {
return (Collection) insets;
}
@Override
public CompositeKeyMappingConfiguration<? super C> getMappedSuperClassConfiguration() {
return superMappingBuilder == null ? null : superMappingBuilder.getConfiguration();
}
@Override
@Nullable
public ColumnNamingStrategy getColumnNamingStrategy() {
return columnNamingStrategy;
}
@Override
public List<CompositeKeyLinkage<C, ?>> getPropertiesMapping() {
return mapping;
}
@Override
public CompositeKeyMappingConfiguration<C> getConfiguration() {
return this;
}
@Override
public Method captureLambdaMethod(SerializableAccessor getter) {
return this.methodSpy.findMethod(getter);
}
@Override
public Method captureLambdaMethod(SerializableMutator setter) {
return this.methodSpy.findMethod(setter);
}
@Override
public FluentCompositeKeyMappingConfigurationSupport<C> withColumnNaming(ColumnNamingStrategy columnNamingStrategy) {
this.columnNamingStrategy = columnNamingStrategy;
return this;
}
/**
* Gives access to currently configured {@link Inset}. Made so one can access features of {@link Inset} which are wider than
* the one available through {@link FluentCompositeKeyMappingBuilder}.
*
* @return the last {@link Inset} built by {@link #newInset(SerializablePropertyAccessor, CompositeKeyMappingConfigurationProvider)}
* or {@link #newInset(SerializablePropertyMutator, CompositeKeyMappingConfigurationProvider)}
*/
protected Inset<C, ?> currentInset() {
return currentInset;
}
protected <O> Inset<C, O> newInset(SerializablePropertyAccessor<C, O> getter, CompositeKeyMappingConfigurationProvider<? extends O> CompositeKeyMappingBuilder) {
currentInset = new Inset<>(getter, CompositeKeyMappingBuilder, this);
return (Inset<C, O>) currentInset;
}
protected <O> Inset<C, O> newInset(SerializablePropertyMutator<C, O> setter, CompositeKeyMappingConfigurationProvider<? extends O> CompositeKeyMappingBuilder) {
currentInset = new Inset<>(setter, CompositeKeyMappingBuilder, this);
return (Inset<C, O>) currentInset;
}
@Override
public <O> FluentCompositeKeyMappingBuilderPropertyOptions<C> map(SerializablePropertyMutator<C, O> setter) {
return wrapWithPropertyOptions(addMapping(setter));
}
@Override
public <O> FluentCompositeKeyMappingBuilderPropertyOptions<C> map(SerializablePropertyAccessor<C, O> getter) {
return wrapWithPropertyOptions(addMapping(getter));
}
<E> LinkageSupport<C, E> addMapping(SerializablePropertyMutator<C, E> setter) {
LinkageSupport<C, E> newLinkage = new LinkageSupport<>(setter);
mapping.add(newLinkage);
return newLinkage;
}
<E> LinkageSupport<C, E> addMapping(SerializablePropertyAccessor<C, E> getter) {
LinkageSupport<C, E> newLinkage = new LinkageSupport<>(getter);
mapping.add(newLinkage);
return newLinkage;
}
<O> FluentCompositeKeyMappingBuilderPropertyOptions<C> wrapWithPropertyOptions(LinkageSupport<C, O> linkage) {
return new MethodReferenceDispatcher()
.redirect(CompositeKeyPropertyOptions.class, new CompositeKeyPropertyOptions() {
@Override
public CompositeKeyPropertyOptions columnName(String name) {
linkage.setColumnName(name);
return null;
}
@Override
public CompositeKeyPropertyOptions columnSize(Size size) {
linkage.setColumnSize(size);
return null;
}
@Override
public CompositeKeyPropertyOptions fieldName(String name) {
linkage.setField(FluentCompositeKeyMappingConfigurationSupport.this.classToPersist, name);
return null;
}
}, true)
.fallbackOn(this)
.build((Class<FluentCompositeKeyMappingBuilderPropertyOptions<C>>) (Class) FluentCompositeKeyMappingBuilderPropertyOptions.class);
}
@Override
public <E extends Enum<E>> FluentCompositeKeyMappingBuilderEnumOptions<C> mapEnum(SerializablePropertyMutator<C, E> setter) {
LinkageSupport<C, E> linkage = addMapping(setter);
return wrapWithEnumOptions(linkage);
}
@Override
public <E extends Enum<E>> FluentCompositeKeyMappingBuilderEnumOptions<C> mapEnum(SerializablePropertyAccessor<C, E> getter) {
LinkageSupport<C, E> linkage = addMapping(getter);
return wrapWithEnumOptions(linkage);
}
<O extends Enum> FluentCompositeKeyMappingBuilderEnumOptions<C> wrapWithEnumOptions(LinkageSupport<C, O> linkage) {
return new MethodReferenceDispatcher()
.redirect(CompositeKeyEnumOptions.class, new CompositeKeyEnumOptions() {
@Override
public CompositeKeyEnumOptions byName() {
linkage.setEnumBindType(EnumBindType.NAME);
return null; // we can return null because dispatcher will return proxy
}
@Override
public CompositeKeyEnumOptions byOrdinal() {
linkage.setEnumBindType(EnumBindType.ORDINAL);
return null; // we can return null because dispatcher will return proxy
}
}, true)
.redirect(CompositeKeyPropertyOptions.class, new CompositeKeyPropertyOptions() {
@Override
public CompositeKeyPropertyOptions columnName(String name) {
linkage.setColumnName(name);
return null;
}
@Override
public CompositeKeyPropertyOptions columnSize(Size size) {
linkage.setColumnSize(size);
return null;
}
@Override
public CompositeKeyPropertyOptions fieldName(String name) {
linkage.setField(FluentCompositeKeyMappingConfigurationSupport.this.classToPersist, name);
return null;
}
}, true)
.fallbackOn(this)
.build((Class<FluentCompositeKeyMappingBuilderEnumOptions<C>>) (Class) FluentCompositeKeyMappingBuilderEnumOptions.class);
}
@Override
public FluentCompositeKeyMappingBuilder<C> mapSuperClass(CompositeKeyMappingConfigurationProvider<? super C> superMappingConfiguration) {
this.superMappingBuilder = superMappingConfiguration;
return this;
}
@Override
public <O> FluentCompositeKeyMappingBuilderCompositeKeyMappingConfigurationImportedEmbedOptions<C, O> embed(
SerializablePropertyAccessor<C, O> getter,
CompositeKeyMappingConfigurationProvider<? extends O> compositeKeyMappingBuilder) {
return addImportedInset(newInset(getter, compositeKeyMappingBuilder));
}
@Override
public <O> FluentCompositeKeyMappingBuilderCompositeKeyMappingConfigurationImportedEmbedOptions<C, O> embed(
SerializablePropertyMutator<C, O> setter,
CompositeKeyMappingConfigurationProvider<? extends O> compositeKeyMappingBuilder) {
return addImportedInset(newInset(setter, compositeKeyMappingBuilder));
}
private <O> FluentCompositeKeyMappingBuilderCompositeKeyMappingConfigurationImportedEmbedOptions<C, O> addImportedInset(Inset<C, O> inset) {
insets.add(inset);
return new MethodReferenceDispatcher()
.redirect(ImportedEmbedOptions.class, new ImportedEmbedOptions<O>() {
@Override
public <IN> ImportedEmbedOptions<O> overrideName(SerializablePropertyAccessor<O, IN> getter, String columnName) {
inset.overrideName(getter, columnName);
return null; // we can return null because dispatcher will return proxy
}
@Override
public <IN> ImportedEmbedOptions<O> overrideName(SerializablePropertyMutator<O, IN> setter, String columnName) {
inset.overrideName(setter, columnName);
return null; // we can return null because dispatcher will return proxy
}
@Override
public <IN> ImportedEmbedOptions<O> overrideSize(SerializablePropertyAccessor<O, IN> getter, Size columnSize) {
inset.overrideSize(getter, columnSize);
return null; // we can return null because dispatcher will return proxy
}
@Override
public <IN> ImportedEmbedOptions<O> overrideSize(SerializablePropertyMutator<O, IN> setter, Size columnSize) {
inset.overrideSize(setter, columnSize);
return null; // we can return null because dispatcher will return proxy
}
@Override
public <IN> ImportedEmbedOptions<O> exclude(SerializablePropertyMutator<O, IN> setter) {
inset.exclude(setter);
return null; // we can return null because dispatcher will return proxy
}
@Override
public <IN> ImportedEmbedOptions<O> exclude(SerializablePropertyAccessor<O, IN> getter) {
inset.exclude(getter);
return null; // we can return null because dispatcher will return proxy
}
}, true)
.fallbackOn(this)
.build((Class<FluentCompositeKeyMappingBuilderCompositeKeyMappingConfigurationImportedEmbedOptions<C, O>>) (Class) FluentCompositeKeyMappingBuilderCompositeKeyMappingConfigurationImportedEmbedOptions.class);
}
/**
* Small contract for mapping definition storage. See add(..) methods.
*
* @param <T> property owner type
*/
protected static class LinkageSupport<T, O> implements CompositeKeyLinkage<T, O> {
/**
* Optional binder for this mapping.
* May be not of column type in cases of converted data with {@link ParameterBinder#preApply(SerializableThrowingFunction)}
* or {@link ParameterBinder#thenApply(SerializableThrowingFunction)}.
*/
private ParameterBinder<Object> parameterBinder;
@Nullable
private EnumBindType enumBindType;
@Nullable
private String columnName;
@Nullable
private Size columnSize;
private final ValueAccessPointVariantSupport<T, O> accessor;
private String fieldName;
public LinkageSupport(SerializablePropertyAccessor<T, O> getter) {
this.accessor = new ValueAccessPointVariantSupport<>(getter);
}
public LinkageSupport(SerializablePropertyMutator<T, O> setter) {
this.accessor = new ValueAccessPointVariantSupport<>(setter);
}
@Override
@Nullable
public ParameterBinder<Object> getParameterBinder() {
return parameterBinder;
}
public void setParameterBinder(@Nullable ParameterBinder<?> parameterBinder) {
this.parameterBinder = (ParameterBinder<Object>) parameterBinder;
}
@Nullable
@Override
public EnumBindType getEnumBindType() {
return enumBindType;
}
public void setEnumBindType(@Nullable EnumBindType enumBindType) {
this.enumBindType = enumBindType;
}
@Nullable
@Override
public String getColumnName() {
return this.columnName;
}
public void setColumnName(String name) {
this.columnName = name;
}
@Nullable
@Override
public Size getColumnSize() {
return this.columnSize;
}
public void setColumnSize(@Nullable Size columnSize) {
this.columnSize = columnSize;
}
@Override
public ReadWritePropertyAccessPoint<T, O> getAccessor() {
return accessor.getAccessor();
}
@Nullable
@Override
public String getFieldName() {
return fieldName;
}
public void setField(Class<T> classToPersist, String fieldName) {
this.fieldName = fieldName;
// Note that getField(..) will throw an exception if field is not found, at the opposite of findField(..)
this.accessor.setField(classToPersist, fieldName);
}
@Override
public Class<O> getColumnType() {
return (Class<O>) AccessorDefinition.giveDefinition(this.accessor.getAccessor()).getMemberType();
}
}
/**
* Information storage of embedded mapping defined externally by an {@link CompositeKeyMappingConfigurationProvider},
* see {@link FluentCompositeKeyMappingConfiguration#embed(SerializablePropertyAccessor, CompositeKeyMappingConfigurationProvider)}
*
* @param <SRC>
* @param <TRGT>
* @see FluentCompositeKeyMappingConfiguration#embed(SerializablePropertyAccessor, CompositeKeyMappingConfigurationProvider) }
* @see FluentCompositeKeyMappingConfiguration#embed(SerializablePropertyMutator, CompositeKeyMappingConfigurationProvider) }
*/
public static class Inset<SRC, TRGT> {
private final Class<TRGT> embeddedClass;
private final Method insetAccessor;
/** Equivalent of {@link #insetAccessor} as a {@link ReadWriteAccessPoint} */
private final ReadWritePropertyAccessPoint<SRC, TRGT> accessor;
private final ValueAccessPointMap<TRGT, String, ValueAccessPoint<TRGT>> overriddenColumnNames = new ValueAccessPointMap<>();
private final ValueAccessPointMap<TRGT, Size, ValueAccessPoint<TRGT>> overriddenColumnSizes = new ValueAccessPointMap<>();
private final ValueAccessPointSet<TRGT, ValueAccessPoint<TRGT>> excludedProperties = new ValueAccessPointSet<>();
private final CompositeKeyMappingConfigurationProvider<? extends TRGT> configurationProvider;
private final ValueAccessPointMap<TRGT, Column, ValueAccessPoint<TRGT>> overriddenColumns = new ValueAccessPointMap<>();
Inset(SerializablePropertyMutator<SRC, TRGT> targetSetter,
CompositeKeyMappingConfigurationProvider<? extends TRGT> configurationProvider,
LambdaMethodUnsheller lambdaMethodUnsheller) {
this.insetAccessor = lambdaMethodUnsheller.captureLambdaMethod(targetSetter);
this.accessor = Accessors.readWriteAccessPoint(targetSetter);
// looking for the target type because it's necessary to find its persister (and other objects)
this.embeddedClass = Reflections.javaBeanTargetType(getInsetAccessor());
this.configurationProvider = configurationProvider;
}
Inset(SerializablePropertyAccessor<SRC, TRGT> targetGetter,
CompositeKeyMappingConfigurationProvider<? extends TRGT> configurationProvider,
LambdaMethodUnsheller lambdaMethodUnsheller) {
this.insetAccessor = lambdaMethodUnsheller.captureLambdaMethod(targetGetter);
this.accessor = Accessors.readWriteAccessPoint(targetGetter);
// looking for the target type because it's necessary to find its persister (and other objects)
this.embeddedClass = Reflections.javaBeanTargetType(getInsetAccessor());
this.configurationProvider = configurationProvider;
}
/**
* Equivalent of {@link #insetAccessor} as a {@link ReadWriteAccessPoint}
*/
public ReadWritePropertyAccessPoint<SRC, TRGT> getAccessor() {
return accessor;
}
/**
* Equivalent of given getter or setter at construction time as a {@link Method}
*/
public Method getInsetAccessor() {
return insetAccessor;
}
public Class<TRGT> getEmbeddedClass() {
return embeddedClass;
}
public ValueAccessPointSet<TRGT, ValueAccessPoint<TRGT>> getExcludedProperties() {
return this.excludedProperties;
}
public ValueAccessPointMap<TRGT, String, ValueAccessPoint<TRGT>> getOverriddenColumnNames() {
return this.overriddenColumnNames;
}
public ValueAccessPointMap<TRGT, Size, ValueAccessPoint<TRGT>> getOverriddenColumnSizes() {
return overriddenColumnSizes;
}
public ValueAccessPointMap<TRGT, Column, ValueAccessPoint<TRGT>> getOverriddenColumns() {
return overriddenColumns;
}
public CompositeKeyMappingConfigurationProvider<TRGT> getConfigurationProvider() {
return (CompositeKeyMappingConfigurationProvider<TRGT>) configurationProvider;
}
public void overrideName(SerializableAccessor<TRGT, ?> methodRef, String columnName) {
this.overriddenColumnNames.put(new AccessorByMethodReference<>(methodRef), columnName);
}
public void overrideName(SerializableMutator<TRGT, ?> methodRef, String columnName) {
this.overriddenColumnNames.put(new MutatorByMethodReference<>(methodRef), columnName);
}
public void overrideName(AccessorChain<TRGT, ?> accessorChain, String columnName) {
this.overriddenColumnNames.put(accessorChain, columnName);
}
public void overrideSize(SerializableAccessor<TRGT, ?> methodRef, Size columnSize) {
this.overriddenColumnSizes.put(new AccessorByMethodReference<>(methodRef), columnSize);
}
public void overrideSize(SerializableMutator<TRGT, ?> methodRef, Size columnSize) {
this.overriddenColumnSizes.put(new MutatorByMethodReference<>(methodRef), columnSize);
}
public void overrideSize(AccessorChain<TRGT, ?> accessorChain, Size columnSize) {
this.overriddenColumnSizes.put(accessorChain, columnSize);
}
public void override(SerializableAccessor<TRGT, ?> methodRef, Column column) {
this.overriddenColumns.put(new AccessorByMethodReference<>(methodRef), column);
}
public void override(SerializableMutator methodRef, Column column) {
this.overriddenColumns.put(new MutatorByMethodReference(methodRef), column);
}
public void exclude(SerializableMutator methodRef) {
this.excludedProperties.add(new MutatorByMethodReference(methodRef));
}
public void exclude(SerializableAccessor methodRef) {
this.excludedProperties.add(new AccessorByMethodReference(methodRef));
}
}
}